/* -*- mode: C; mode: folding; fill-column: 70; -*- */
#define _XOPEN_SOURCE 600
#define _LARGEFILE64_SOURCE 1
#define _FILE_OFFSET_BITS 64

#include "stinger-atomics.h"
#include "stinger-utils.h"
#include "stinger.h"
#include "timer.h"
#include "xmalloc.h"

#define ACTI(k) (action[2*(k)])
#define ACTJ(k) (action[2*(k)+1])

static int64_t nv, ne, naction;
static int64_t * restrict off;
static int64_t * restrict from;
static int64_t * restrict ind;
static int64_t * restrict weight;
static int64_t * restrict action;

/* handles for I/O memory */
static int64_t * restrict graphmem;
static int64_t * restrict actionmem;

static char * initial_graph_name = INITIAL_GRAPH_NAME_DEFAULT;
static char * action_stream_name = ACTION_STREAM_NAME_DEFAULT;

//static long batch_size = BATCH_SIZE_DEFAULT;
static long batch_size = 100;
//static long nbatch = NBATCH_DEFAULT;
static long nbatch = 10;

static struct stinger * S;

static double * update_time_trace;

int setBFS(const struct stinger *G, const int64_t nv, const int64_t ne, const int64_t s, const int64_t numSteps , int *countK31InitGraph);

int
main (const int argc, char *argv[])
{

  /* Variable declaration */
  int countK31InitGraph = 0;
  int countK33InitGraph = 0; 

  int64_t * act = xmalloc (2 * 2 * batch_size * sizeof(*act));
  int64_t * insoff = xmalloc ((2 * 2 * batch_size + 1) * sizeof(*insoff));
  int64_t * deloff = xmalloc ((2 * 2 * batch_size + 1) * sizeof(*deloff));

  parse_args (argc, argv, &initial_graph_name, &action_stream_name, &batch_size, &nbatch);
  STATS_INIT();

  load_graph_and_action_stream (initial_graph_name, &nv, &ne, (int64_t**)&off,
	      (int64_t**)&ind, (int64_t**)&weight, (int64_t**)&graphmem,
	      action_stream_name, &naction, (int64_t**)&action, (int64_t**)&actionmem);

  print_initial_graph_stats (nv, ne, batch_size, nbatch, naction);
  BATCH_SIZE_CHECK();

  update_time_trace = xmalloc (nbatch * sizeof(*update_time_trace));

  /* Convert to STINGER */
  tic ();
  S = stinger_new ();
  
  stinger_set_initial_edges (S, nv, 0, off, ind, weight, NULL, NULL, 0);
  PRINT_STAT_DOUBLE ("time_stinger", toc ());
  
  fflush(stdout);
  free(graphmem);

  tic ();
  uint32_t errorCode = stinger_consistency_check (S, nv);
  double time_check = toc ();
  printf("\n\t\"error_code\": 0x%lx", errorCode);
  PRINT_STAT_DOUBLE ("time_check", time_check);

  int64_t numSteps = 3;

  /* Randomly choose a vertex to start with */
  int64_t startV = 124;

  /* Perform bfs on csr format graph to find K3,1 */
  printf("\nPerforming breadth first search on graph to find 3,1 Bipartite graph\n");
  setBFS(S , nv , ne , startV , numSteps , &countK31InitGraph);
  printf("\n\nNumber of K3,1 in Initial graph : %d\n", countK31InitGraph);

  /* Updates */
  int64_t ntrace = 0;

  for (int64_t actno = 0; actno < nbatch * batch_size; actno += batch_size)
  {
    tic();

    const int64_t endact = (actno + batch_size > naction ? naction : actno + batch_size);
    int64_t *actionStream = &action[2*actno];
    int64_t numActions = endact - actno;

    int edgeAddtions = 0;
    int edgeDeletions = 0;
    for (int64_t k = 0; k < numActions; k++)
    {
      const int64_t i = actionStream[2 * k];
      const int64_t j = actionStream[2 * k + 1];

      /* This is where you work on each edge action. */
      if (i >= 0 && j >= 0) {edgeAddtions++;}
      if (i < 0  && j < 0 ) {edgeDeletions++;}
 
    }

    int64_t N = stinger_sort_actions (endact - actno, &action[2*actno], insoff, deloff, act);
    stinger_remove_and_insert_batch (S, 0, actno+1, N, insoff, deloff, act);
    int tcountK31InitGraph = 0;

    printf("After %d additions and %d deletions:\n", edgeAddtions, edgeDeletions);
    tcountK31InitGraph = setBFS(S , nv , ne , startV , numSteps , &countK31InitGraph);
    
    update_time_trace[ntrace] = toc();
    ntrace++;
 
  } /* End of batch */



  /* Print the times */
  double time_updates = 0;
  for (int64_t k = 0; k < nbatch; k++) {
    time_updates += update_time_trace[k];
  }
  PRINT_STAT_DOUBLE ("time_updates", time_updates);
  PRINT_STAT_DOUBLE ("updates_per_sec", (nbatch * batch_size) / time_updates); 

  tic ();
  errorCode = stinger_consistency_check (S, nv);
  time_check = toc ();
  printf("\n\t\"error_code\": 0x%lx", errorCode);
  PRINT_STAT_DOUBLE ("time_check", time_check);

  free(update_time_trace);
  stinger_free_all (S);
  free (actionmem);
  STATS_END();
}
